#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/7/11 18:03
# @File    : body.py
# @Description : -i http://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn

# -*- coding: utf-8 -*-
"""
@Time : 2024/3/18 0:17
@Auth : Junjie Duan
@File : body.py
    -i https://pypi.tuna.tsinghua.edu.cn/simple
    https://emojipedia.org/
    https://streamlit-emoji-shortcodes-streamlit-app-gwckff.streamlit.app/
"""

import tkinter as tk

import time

import numpy as np
import ttkbootstrap as ttk
import matplotlib.pyplot as plt


from random import choice
from matplotlib import animation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from ttkbootstrap import utility, Querybox
from random import choices

from ttkbootstrap.dialogs.colorchooser import ColorChooserDialog
from ttkbootstrap.style import Bootstyle
from tkinter.filedialog import askdirectory
from ttkbootstrap.dialogs import Messagebox
from ttkbootstrap.constants import *
from tkinter.scrolledtext import ScrolledText
from ttkbootstrap.scrolled import ScrolledText, ScrolledFrame
from pathlib import Path
utility.enable_high_dpi_awareness()

#plt.style.use('seaborn-darkgrid')
#plt.style.use('fivethirtyeight')


DARK = 'superhero'
LIGHT = 'flatly'

def create_sizegrip_style(bootstyle, style):

    frame = ttk.Frame(root, padding=5)
    # title
    title = ttk.Label(
        master=frame,
        text='Sizegrip',
        anchor=tk.CENTER
    )
    title.pack(padx=5, pady=2, fill=tk.BOTH)
    ttk.Separator(frame).pack(padx=5, pady=5, fill=tk.X)

    # back_me_up.py
    # test_date_entry.py
    # collapsing_frame.py 折叠
    # test_labelframe.py 带标签的frame
    # test_menu.py # 右键
    # test_notebook.py # 书签
    # test_optionmenu.py # 下拉框
    # test_panedwindow.py 手动控制大小
    ttk.Label(frame, text="123").pack(fill=tk.X)
    sg = ttk.Sizegrip(frame)
    sg.pack(padx=5, pady=5, fill=tk.BOTH, expand=True)

    return frame



def change_style():
    theme = choice(style.theme_names())
    print(theme)
    style.theme_use(theme)


class CollapsingFrame(ttk.Frame):
    """A collapsible frame widget that opens and closes with a click."""

    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)
        self.columnconfigure(0, weight=1)
        self.cumulative_rows = 0

        # widget images
        self.images = [
            ttk.PhotoImage(file='./image/icons8_double_up_24px.png'),
            ttk.PhotoImage(file='./image/icons8_double_right_24px.png')
        ]

    def add(self, child, title="", bootstyle=PRIMARY, **kwargs):
        """Add a child to the collapsible frame

        Parameters:

            child (Frame):
                The child frame to add to the widget.

            title (str):
                The title appearing on the collapsible section header.

            bootstyle (str):
                The style to apply to the collapsible section header.

            **kwargs (Dict):
                Other optional keyword arguments.
        """
        if child.winfo_class() != 'TFrame':
            return

        style_color = Bootstyle.ttkstyle_widget_color(bootstyle)
        frm = ttk.Frame(self, bootstyle=style_color)
        frm.grid(row=self.cumulative_rows, column=0, sticky=EW)

        # header title
        header = ttk.Label(
            master=frm,
            text=title,
            bootstyle=(style_color, INVERSE)
        )
        if kwargs.get('textvariable'):
            header.configure(textvariable=kwargs.get('textvariable'))
        header.pack(side=LEFT, fill=BOTH, padx=10)

        # header toggle button
        def _func(c=child):
            return self._toggle_open_close(c)

        btn = ttk.Button(
            master=frm,
            image=self.images[0],
            bootstyle=style_color,
            command=_func
        )
        btn.pack(side=RIGHT)

        # assign toggle button to child so that it can be toggled
        child.btn = btn
        child.grid(row=self.cumulative_rows + 1, column=0, sticky=NSEW)

        # increment the row assignment
        self.cumulative_rows += 2

    def _toggle_open_close(self, child):
        """Open or close the section and change the toggle button
        image accordingly.

        Parameters:

            child (Frame):
                The child element to add or remove from grid manager.
        """
        if child.winfo_viewable():
            child.grid_remove()
            child.btn.configure(image=self.images[1])
        else:
            child.grid()
            child.btn.configure(image=self.images[0])


class DraggableFrame(ttk.Frame):
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)
        self.master = master
        self.bind("<Button-1>", self.on_start_drag)
        self.bind("<Button-3>", self.popup) # 右键事件
        self.bind("<B1-Motion>", self.on_dragging)
        self.bind("<ButtonRelease-1>", self.on_stop_drag)
        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)
        self._drag_data = {"x": 0, "y": 0}
        # 定义一个全局变量来跟踪鼠标是否正在标签上滑过
        self.is_hovering = False

    def on_start_drag(self, event):
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def on_enter(self, event):
        # 鼠标进入时改变背景色
        self.is_hovering = True
        print("鼠标进来了")

        # 安排一个回调函数在1秒后执行，以恢复背景色
        self.after(1000, self.on_leave)

    def on_leave(self, event=None):
        self.is_hovering = False
        # 鼠标离开时恢复背景色
        print("鼠标离开")

    def popup(self, event):
        # 创建一个弹出菜单
        popup_menu = tk.Menu(self, tearoff=0)
        popup_menu.add_command(label="信息", command=lambda: self.info())
        popup_menu.add_command(label="删除", command=lambda: self.delete())
        # 显示弹出菜单
        popup_menu.tk_popup(event.x_root, event.y_root)

    def info(self):
        info = {
            'class': self.winfo_class(),
            'id': self.winfo_id(),
            'name': self.winfo_name(),
            'parent': self.winfo_parent(),
            'x': self.winfo_x(),
            'y': self.winfo_y(),
            'width': self.winfo_width(),
            'height': self.winfo_height(),
            'config': self.config()  # 获取所有配置选项和它们的值
        }
        Messagebox.ok(info.__str__(), position=(500, 500))
        print(info)

    def delete(self):
        print("删除事件")
        self.destroy()

    def clear_all_widgets(self):
        print("删除当前frame下所有子组件")
        for widget in self.winfo_children():
            widget.destroy()

    def on_dragging(self, event):
        delta_x = event.x - self._drag_data["x"]
        delta_y = event.y - self._drag_data["y"]
        x = self.winfo_x() + delta_x
        y = self.winfo_y() + delta_y

        print("-------------------")
        parent_width = self.master.winfo_width()
        parent_height = self.master.winfo_height()
        current_frame_width = self.winfo_width()
        current_frame_height = self.winfo_height()
        print(f"current frame size ({current_frame_width},{current_frame_height})")
        print(f"parent Frame Width: {parent_width} pixels Height: {parent_height} pixels")
        # 最大不可 超出frame最大高度 最少暴漏出10个像素点
        x = x if parent_width > x else parent_width - 10
        y = y if parent_height > y else parent_height - 10
        x = -current_frame_width+10 if x <= -current_frame_width+10 else x
        y = -current_frame_height+10 if y <= -current_frame_height+10 else y
        print(f"move to ({x},{y})")

        self.place(x=x, y=y)

    def on_stop_drag(self, event):
        print("停止移动")
        pass  # 可以在这里添加停止拖动时的处理逻辑


class Stopwatch(ttk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.pack(fill=BOTH, expand=YES)
        self.running = ttk.BooleanVar(value=False)
        self.afterid = ttk.StringVar()
        self.elapsed = ttk.IntVar()
        self.stopwatch_text = ttk.StringVar(value="00:00:00")

        self.create_stopwatch_label()
        self.create_stopwatch_controls()

    def create_stopwatch_label(self):
        """Create the stopwatch number display"""
        lbl = ttk.Label(
            master=self,
            font="-size 32",
            anchor=CENTER,
            textvariable=self.stopwatch_text,
        )
        lbl.pack(side=TOP, fill=X, padx=60, pady=20)

    def create_stopwatch_controls(self):
        """Create the control frame with buttons"""
        container = ttk.Frame(self, padding=10)
        container.pack(fill=X)
        self.buttons = []
        self.buttons.append(
            ttk.Button(
                master=container,
                text="Start",
                width=10,
                bootstyle=INFO,
                command=self.on_toggle,
            )
        )
        self.buttons.append(
            ttk.Button(
                master=container,
                text="Reset",
                width=10,
                bootstyle=SUCCESS,
                command=self.on_reset,
            )
        )
        self.buttons.append(
            ttk.Button(
                master=container,
                text="Quit",
                width=10,
                bootstyle=DANGER,
                command=self.on_quit,
            )
        )
        for button in self.buttons:
            button.pack(side=LEFT, fill=X, expand=YES, pady=10, padx=5)

    def on_toggle(self):
        """Toggle the start and pause button."""
        button = self.buttons[0]
        if self.running.get():
            self.pause()
            self.running.set(False)
            button.configure(bootstyle=INFO, text="Start")
        else:
            self.start()
            self.running.set(True)
            button.configure(bootstyle=(INFO, OUTLINE), text="Pause")

    def on_quit(self):
        """Quit the application."""
        self.quit()

    def on_reset(self):
        """Reset the stopwatch number display."""
        self.elapsed.set(0)
        self.stopwatch_text.set("00:00:00")

    def start(self):
        """Start the stopwatch and update the display."""
        self.afterid.set(self.after(1, self.increment))

    def pause(self):
        """Pause the stopwatch"""
        self.after_cancel(self.afterid.get())

    def increment(self):
        """Increment the stopwatch value. This method continues to
        schedule itself every 1 second until stopped or paused."""
        current = self.elapsed.get() + 1
        self.elapsed.set(current)
        formatted = "{:02d}:{:02d}:{:02d}".format(
            (current // 100) // 60, (current // 100) % 60, (current % 100)
        )
        self.stopwatch_text.set(formatted)
        self.afterid.set(self.after(100, self.increment))


def get_pyplot(frame):

    # 创建一个matplotlib的Figure对象
    fig = Figure(figsize=(5, 4), dpi=100)
    ax = fig.add_subplot(111)
    ax.set_xlim(0, 2 * np.pi)
    ax.set_ylim(-1, 1)
    line, = ax.plot([], [], lw=2)


    # 初始化函数，设置图表的数据为空
    def init():
        line.set_data([], [])
        return line,


    # 更新函数，用于动态更新图表数据
    def update(frame):
        # 这地方需要用到异步 不然界面会特别卡
        x = np.linspace(0, 2 * np.pi, 100)
        y = np.sin(x + 2 * np.pi * frame / 100.0)
        line.set_data(x, y)
        return line,


    # 创建一个FigureCanvasTkAgg部件
    canvas = FigureCanvasTkAgg(fig, master=frame)  # 使用frame作为master
    canvas.draw()
    canvas_widget = canvas.get_tk_widget()

    # 将Canvas部件放入ttk Frame中
    #canvas_widget.grid(column=0, row=0, sticky="nsew")
    #canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
    canvas_widget.pack(side=tk.TOP, fill=tk.BOTH)

    # 使用FuncAnimation创建动画
    ani = animation.FuncAnimation(fig, update, frames=range(100),
                                  init_func=init, blit=True, interval=20)
    return ani



if __name__ == '__main__':
    # create visual widget style tests
    root = tk.Tk()

    root_width = 1600
    root_height = 900
    root_x, root_y = 500, 200
    root.geometry(f"{root_width}x{root_height}+{root_x}+{root_y}")
    style = ttk.Style()

    #ttk.Button(text="Change Theme", command=change_style).pack(padx=10, pady=10)

    # create_sizegrip_style('', style).pack(
    #     side=tk.LEFT, fill=tk.BOTH, expand=True
    # )

    #Console(root)

    # top = ttk.Frame(root, bootstyle="warning")
    # top.pack(padx=10, pady=10, expand=True, fill='both')
    #
    # body = ttk.Frame(root, bootstyle="danger")
    # body.pack(padx=10, pady=10, expand=True, fill='both')
    #
    # bottom = ttk.Frame(root, bootstyle="success")
    # bottom.pack(padx=10, pady=10, expand=True, fill='both')

    print(" ========top ===== ")
    buttonbar = ttk.Frame(root, style='primary.TFrame')
    buttonbar.pack(fill=X, pady=1, side=TOP)
    _func = lambda: Messagebox.ok(message='Adding new backup')
    btn = ttk.Button(
        master=buttonbar, text='New backup set',
        compound=LEFT,
        command=_func
    )
    btn.pack(side=LEFT, ipadx=5, ipady=5, padx=(1, 0), pady=1)

    _func = lambda: Messagebox.ok(message='Backing up...')
    btn = ttk.Button(
        master=buttonbar,
        text='Backup',
        #image='play',
        compound=LEFT,
        command=_func
    )
    btn.pack(side=LEFT, ipadx=5, ipady=5, padx=0, pady=1)

    btn = ttk.Button(master=buttonbar, text="退出", command=root.destroy)
    btn.pack(side=RIGHT, ipadx=5, ipady=5, padx=0, pady=1)

    btn = ttk.Button(master=buttonbar, text="主题切换", command=change_style)
    btn.pack(side=RIGHT, ipadx=5, ipady=5, padx=0, pady=1)



    print("=======body========")
    body = ttk.Frame(root, style='bg.TFrame', bootstyle="danger")
    body.pack(side=TOP, fill=BOTH, expand=True)

    print("======body left===")
    left_panel = ttk.Frame(body, style='bg.TFrame')
    left_panel.pack(side=LEFT, fill=Y)
    #left_panel.grid(row=0, column=0, sticky=NSEW)

    ## backup summary (collapsible)
    bus_cf = CollapsingFrame(left_panel)
    bus_cf.pack(fill=Y, pady=1)
    ## container
    bus_frm = ttk.Frame(bus_cf, padding=1)
    bus_frm.columnconfigure(0, weight=1)
    bus_cf.add(
        child=bus_frm,
        title='left',
        bootstyle=SECONDARY
    )

    cleaner_btn = ttk.Button(
        master=bus_frm,
        #image='cleaner',
        text='cleaner',
        compound=TOP,
        bootstyle='light'
    )
    cleaner_btn.pack(side=TOP, fill=BOTH, ipadx=10, ipady=10)

    registry_btn = ttk.Button(
        master=bus_frm,
        #image='registry',
        text='registry',
        compound=TOP,
        bootstyle='light'
    )
    registry_btn.pack(side=TOP, fill=BOTH, ipadx=10, ipady=10)

    tools_btn = ttk.Button(
        master=bus_frm,
        #image='tools',
        text='tools',
        compound=TOP,
        bootstyle='light'
    )
    tools_btn.pack(side=TOP, fill=BOTH, ipadx=10, ipady=10)

    options_btn = ttk.Button(
        master=bus_frm,
        #image='options',
        text='options',
        compound=TOP,
        bootstyle='light'

    )
    options_btn.pack(side=TOP, fill=BOTH, ipadx=10, ipady=10)


    tv = ttk.Treeview(
        master=bus_frm,
        #bootstyle="default",
        height=5
    )
    tv.column('#0')
    tv.heading('#0', text="标题", anchor=tk.W)
    iid = tv.insert('', tk.END, text='parent 1')
    tv.insert(iid, tk.END, text='child 1')
    tv.insert(iid, tk.END, text='child 2')
    tv.insert(iid, tk.END, text='child 2')
    tv.insert('', tk.END, text='parent 2')
    #tv.pack(padx=5, pady=5)
    tv.selection_add(iid)

    #tv.pack(padx=0, pady=0, side=tk.LEFT, fill=BOTH)
    tv.pack(side=TOP, fill=BOTH)


    print("======body center===")
    # center_panel = ttk.Frame(body, padding=(2, 1), bootstyle="success")
    # center_panel.pack(side=LEFT, fill=BOTH, expand=True)

    nb = ttk.Notebook(body, bootstyle="light")
    #nb.pack(padx=5, pady=5, fill=tk.BOTH)
    nb.pack(padx=1, pady=1, side=LEFT, fill=BOTH, expand=True)

    # for i, _ in enumerate(style.colors):
    #     nb.add(ttk.Frame(nb), text=f'Tab {i+1}')
    center_panel = ttk.Frame(nb, padding=(1, 1))
    nb.add(center_panel, text="首页")

    page2 = ttk.Frame(nb, padding=(1, 1))
    ttk.Button(page2, text="打开首页", command=lambda: nb.select(0)).pack(side=TOP, ipadx=5, ipady=5)
    nb.add(page2, text='页面2')

    page3 = ttk.Frame(nb, padding=(1, 1))
    draggable_frame = DraggableFrame(page3, width=100, height=50, bootstyle='danger')
    draggable_frame.place(x=10, y=10)
    nb.add(page3, text="可拖动frame")

    page4 = ttk.Frame(nb, padding=(1, 1))
    Stopwatch(page4)
    nb.add(page4, text="计数器")

    page5 = ttk.Frame(nb, padding=(1, 1))
    image = get_pyplot(page5)
    nb.add(page5, text="绘制图表")


    # 默认选择第2页（索引从0开始）
    nb.select(1)

    browse_frm = ttk.Frame(center_panel)
    browse_frm.pack(side=TOP, fill=BOTH, padx=1, pady=1)

    print("====body center  搜索框==")
    path = ttk.StringVar()
    file_entry = ttk.Entry(browse_frm, textvariable=path)
    file_entry.pack(side=LEFT, fill=X, expand=YES)
    btn = ttk.Button(
        master=browse_frm,
        #image='opened-folder',
        text='查询',
        #bootstyle=(LINK, SECONDARY),
    )
    btn.pack(side=RIGHT)

    print("=====body center 表格==")
    tv = ttk.Treeview(center_panel, show='headings', height=5)
    tv.configure(columns=(
        'name', 'state', 'last-modified',
        'last-run-time', 'size'
    ))
    tv.column('name', width=50, stretch=True)
    for col in ['last-modified', 'last-run-time', 'size']:
        tv.column(col, stretch=False)
    for col in tv['columns']:
        tv.heading(col, text=col.title(), anchor=W)
    tv.pack(fill=BOTH, pady=1, expand=YES)

    print("======body right===")
    right_panel = ttk.Frame(body, style='bg.TFrame')
    right_panel.pack(side=LEFT, fill=Y)
    #right_panel.grid(row=0, column=8, sticky=NSEW)

    # right_panel = ttk.Frame(body, padding=(2, 1), bootstyle="success")
    # right_panel.pack(side=RIGHT, fill=BOTH)

    ## backup summary (collapsible)
    bus_cf = CollapsingFrame(right_panel, bootstyle="warning")
    bus_cf.pack(fill=X, pady=1)

    ## container
    bus_frm = ttk.Frame(bus_cf, padding=5)
    bus_frm.columnconfigure(1, weight=1)
    bus_cf.add(
        child=bus_frm,
        title='right',
        bootstyle=SECONDARY
    )

    ## destination
    lbl = ttk.Label(bus_frm, text='Destination:')
    lbl.pack(padx=10, pady=10, fill=tk.X)
    ## last run
    var = tk.Variable()
    om = ttk.OptionMenu(bus_frm, var, 'default', *style.colors)
    om.pack(padx=10, pady=10, fill=tk.X)

    cb = ttk.Checkbutton(bus_frm, text='default2', bootstyle='')
    cb.pack(padx=5, pady=5, fill=tk.BOTH)
    cb.invoke()
    cb = ttk.Checkbutton(bus_frm, text='default3', bootstyle='round-toggle')
    cb.pack(padx=5, pady=5, fill=tk.BOTH)
    cb.invoke()
    cb = ttk.Checkbutton(bus_frm, text='default4', bootstyle='square-toggle')
    cb.pack(padx=5, pady=5, fill=tk.BOTH)
    cb.invoke()
    ttk.Separator(bus_frm).pack(padx=5, pady=5, fill=tk.X)
    # default
    ttk.Label(bus_frm, text='default5').pack()
    entry = ttk.DateEntry(bus_frm)
    entry.pack(padx=5, pady=5, fill=tk.BOTH)

    lbl = ttk.Labelframe(
        master=bus_frm,
        text='default6',
        #bootstyle='',
        width=150,
        height=75
    )
    lbl.pack(padx=5, pady=5, fill=tk.BOTH)

    pb = ttk.Progressbar(
        master=lbl,
        value=25 + ((0 - 1) * 10),
        bootstyle=("success", 'striped'), # striped 有条纹
        orient="horizontal" # horizontal 水平， vertical 横向
    )
    pb.pack(padx=5, pady=5, fill=tk.Y)
    pb.start()

    cb = ttk.Radiobutton(lbl, text='default7', bootstyle='', value=0)
    cb.configure(variable=var)
    cb.pack(padx=5, pady=5, fill=tk.BOTH)
    cb.invoke()

    var = tk.Variable()
    cb = ttk.Radiobutton(lbl, text='default8', bootstyle='outline-toolbutton', value=0)
    cb.configure(variable=var)
    cb.pack(padx=5, pady=5, fill=tk.BOTH)
    #cb.invoke()

    pb = ttk.Scale(
        master=lbl,
        orient=HORIZONTAL,
        value=0.2
    )
    pb.pack(padx=5, pady=5, fill=tk.BOTH)

    def info_():
        top = tk.Toplevel(body)
        frm = ttk.Frame(top, style='primary', width=300, height=300)
        frm.pack(fill=BOTH, padx=5, pady=5, expand=YES)
        ttk.Label(frm, text="我是弹出的").pack(side=TOP, padx=10, pady=10, fill=tk.BOTH, expand=YES)

    ttk.Button(
        master=lbl,
        text='子菜单',
        command=lambda :info_(),
        bootstyle="outline"
    ).pack(padx=5, pady=5, fill=tk.BOTH)

    def getColor():
        cd = ColorChooserDialog(root, initialcolor='#adadad')
        cd.show()
    ttk.Button(
        master=lbl,
        text='获取颜色',
        command=lambda :getColor(),
        bootstyle="outline"
    ).pack(padx=5, pady=5, fill=tk.BOTH)

    def getAge():
        result = Querybox.get_integer("What is your age?")
    ttk.Button(
        master=lbl,
        text='获取年龄',
        command=lambda :getAge(),
        bootstyle="outline"
    ).pack(padx=5, pady=5, fill=tk.BOTH)


    print("=======bottom底部=======")
    bottom = ttk.Frame(root, style='primary.TFrame', bootstyle="success")
    bottom.pack(fill=X, pady=1, side=BOTTOM)

    text_frame = ttk.Frame(bottom, bootstyle="dark")
    text_frame.pack(side=TOP, fill=X, expand=True)

    textbox = tk.Text(text_frame, bg='black', height=5, font='helvetica 8 bold')
    #textbox = ScrolledText(text_frame, padding=5, height=5, hbar=True, autohide=True)
    # textbox.insert(END, 'Insert your text here.')
    # textbox.text.configure(state=DISABLED)
    # textbox.vbar.configure(bootstyle=ROUND)

    textbox.pack(side=LEFT, fill=BOTH, expand=True)
    for i in range(20):
        textbox.insert('end', f'{i}.Hello, this is my text.\n')

    textbox.insert(END, 'Insert your text here.')
    textbox.yview('end')  # Text 滚动到Text组件底部
    textbox.config(state='disabled')  # Text 设置组件为不可编辑状态，防止用户修改



    sg_frame = ttk.Frame(bottom, padding=5)
    sg_frame.pack(side=BOTTOM, fill=X)

    lbl = ttk.Label(sg_frame, text='版本：v1.0', font='helvetica 6 bold')
    lbl.pack(side=LEFT, padx=5, pady=5)

    sg = ttk.Sizegrip(sg_frame)
    sg.pack(side=RIGHT, padx=5, pady=5, fill=tk.BOTH, expand=True)


    #top.pack_propagate(0)
    # ttk.Label(
    #     master=top,
    #     text='success',
    #     bootstyle=('success', 'inverse')
    # ).place(x=0, y=0)


    root.mainloop()
